//
//  Tmp.cpp
//  TestGL
//
//  Created by Bleach on 2019/3/18.
//  Copyright © 2019年 yaong. All rights reserved.
//

#include "Cubic.hpp"

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

const int screenWidth  = 800;
const int screenHeight = 600;

Cubic* Cubic::getInstance()
{
    static Cubic _instance;
    return &_instance;
}

void Cubic::doTest()
{
    
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
    
    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return;
    }
    
    glEnable(GL_DEPTH_TEST);
    
    Shader ourShader("Output/res/6.3.coordinate_systems.vs", "Output/res/6.3.coordinate_systems.fs");
    
    float cubicVec[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    
    glm::vec3 cubicPos[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    
    unsigned int VBO2,VAO2;
    glGenVertexArrays(1, &VAO2);
    glGenBuffers(1, &VBO2);
    glBindVertexArray(VAO2);
    glBindBuffer(GL_ARRAY_BUFFER, VBO2);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubicVec), cubicVec, GL_STATIC_DRAW);
    glVertexAttribPointer(0,3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE, 5* sizeof(float),(void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    
    unsigned int texture21,texture22;
    glGenTextures(1,&texture21);
    glBindTexture(GL_TEXTURE_2D, texture21);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    int t_width, t_height,t_channnel;
    unsigned char *data = stbi_load("Output/res/container.jpg", &t_width, &t_height, &t_channnel, 0);
    if (data)
    {
        //生成纹理
        //arg1-纹理目标，生成与当前半丁的纹理对象在同一目标上
        //arg2-指定多级渐远纹理的级别
        //arg3-设置纹理存储的格式
        //arg4、arg5-纹理宽度和高度
        //arg6-历史遗留固定参数0
        //arg7-源图的格式
        //arg8-数据类型
        //arg9-图像数据
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, t_width, t_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        //为当前绑定的纹理自动生成所需要的多级渐远纹理
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    //释放原图内存
    stbi_image_free(data);
    
    glGenTextures(1,&texture22);
    glBindTexture(GL_TEXTURE_2D, texture22);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    data = stbi_load("Output/res/awesomeface.png", &t_width, &t_height, &t_channnel, 0);
    if (data)
    {
        //生成纹理
        //arg1-纹理目标，生成与当前半丁的纹理对象在同一目标上
        //arg2-指定多级渐远纹理的级别
        //arg3-设置纹理存储的格式
        //arg4、arg5-纹理宽度和高度
        //arg6-历史遗留固定参数0
        //arg7-源图的格式
        //arg8-数据类型
        //arg9-图像数据
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, t_width, t_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        //为当前绑定的纹理自动生成所需要的多级渐远纹理
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    //释放原图内存
    stbi_image_free(data);
    
    glActiveTexture(GL_TEXTURE0);//在绑定纹理之前，先激活纹理单元
    glBindTexture(GL_TEXTURE_2D, texture21);//绑定当前激活的纹理单元
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture22);
    
    ourShader.use();
    ourShader.setInt("texture1", 0);
    ourShader.setInt("texture2", 1);
    
    while (!glfwWindowShouldClose (window))
    {
        //按键监听
        processInput(window);
        
        //预备绘制，填充背景色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        /*
         //颜色渐变的曲线函数
         double timeValue = glfwGetTime();
         float greenValue = (sin((float)timeValue) / 2.0f) + 0.5f;
         float redValue = (cos((float)timeValue) / 2.0f) + 0.5f;
         //传值设置rgb
         myShader.set3Float("ourColor", redValue, greenValue, 1.0f);
         */
        /*
        myShader.use();
        
        glBindVertexArray(VAO);
        
        //只有一张纹理的时候默认激活0位置的默认纹理单元，
        glActiveTexture(GL_TEXTURE0);//在绑定纹理之前，先激活纹理单元
        //绑定当前激活的纹理单元
        glBindTexture(GL_TEXTURE_2D, texture);
        
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        
        //1.move right down and rotate by time;右下角随时间旋转
        glm::mat4 trans2 = glm::mat4(1);
        trans2 = glm::translate(trans2, glm::vec3(0.5f,-0.5f,0.0f));
        trans2 = glm::rotate(trans2, (float)glfwGetTime(), glm::vec3(0.0f,0.0f,1.0f));
        glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(trans2));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(t_mat4));
        glUniformMatrix4fv(rotateLoc, 1, GL_FALSE, glm::value_ptr(t_mat4));
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制六个点，一对三角形
        
        //2.second transformation, move left up and scale by time；左下角随时间缩放
        trans2 = glm::mat4(1.0f);
        trans2 = glm::translate(trans2, glm::vec3(-0.5f,0.5f, 0.0f));
        float fScale = sin(glfwGetTime());
        trans2 = glm::scale(trans2, glm::vec3(fScale,fScale,fScale));
        glUniformMatrix4fv(transLoc, 1, GL_FALSE, &trans2[0][0]);
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(t_mat4));
        glUniformMatrix4fv(rotateLoc, 1, GL_FALSE, glm::value_ptr(t_mat4));
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制六个点，一对三角形
        
        //3.中间带投影旋转45度
        //位移
        glUniformMatrix4fv(transLoc,1,GL_FALSE,&translate[0][0]);
        //投影
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        //旋转
        glUniformMatrix4fv(rotateLoc, 1, GL_FALSE, glm::value_ptr(rotate));
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制六个点，一对三角形
        */
        //绘制一个三角形
        //glDrawArrays(GL_TRIANGLES, 0, 3);
        
        ourShader.use();
        glm::mat4 t_trans = glm::mat4(1.0);
        glm::mat4 t_proj  = glm::mat4(1.0f);

        t_proj = glm::perspective(glm::radians(45.0f), (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
        t_trans       = glm::translate(t_trans, glm::vec3(0.0f, 0.0f, -3.0f));
        ourShader.setMat4("projection", t_proj);
        ourShader.setMat4("translate", t_trans);
        
        // render boxes
        glBindVertexArray(VAO2);

        for (unsigned int i = 0; i < 10; i++)
        {
            // calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 t_trans = glm::mat4(1.0f);
            t_trans = glm::translate(t_trans, cubicPos[i]);
            float angle = 20.0f * i;
            glm::mat4 t_rotate = glm::mat4(1.0f);
            t_trans = glm::rotate(t_trans, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
            ourShader.setMat4("rotate", t_trans);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        
        //清理缓存
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    //清理缓冲内存
    glDeleteVertexArrays(1, &VAO2);
    glDeleteBuffers(1, &VBO2);
    
    glfwTerminate();
}

void Cubic::processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

//窗口大小变化时回调，刷新绘制区域
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    //调整视口跟窗口大小匹配
    glViewport(0, 0, width, height);
}

void Cubic::printMat4(glm::mat4& trans ,const char* strTag)
{
    std::cout<<"========= "<<strTag<<" ========="<<std::endl;
    std::cout<<">>>>>>>>> Print Begin >>>>>>>>>"<<std::endl;
    for(int i = 0;i<4;i++)
    {
        for (int j = 0; j < 4; j++) {
            std::cout<< trans[i][j] << "    ";
        }
        std::cout<<std::endl;
    }
    std::cout<<"<<<<<<<<<< Print Begin <<<<<<<<<<"<<std::endl<<std::endl;
}

void Cubic::printVec(glm::vec1& vec, const char* strTag)
{
    std::cout<<"========= "<<strTag<<" ========="<<std::endl;
    std::cout<< vec.x << std::endl;
}
void Cubic::printVec(glm::vec2& vec, const char* strTag)
{
    std::cout<<"========= "<<strTag<<" ========="<<std::endl;
    std::cout<< vec.x << "  "<< vec.y << std::endl;
}
void Cubic::printVec(glm::vec3& vec, const char* strTag)
{
    std::cout<<"========= "<<strTag<<" ========="<<std::endl;
    std::cout<< vec.x << "  "<< vec.y << "  "<< vec.z << std::endl;
}
void Cubic::printVec(glm::vec4& vec, const char* strTag)
{
    std::cout<<"========= "<<strTag<<" ========="<<std::endl;
    std::cout<< vec.x << "  "<< vec.y<< "  " << vec.z << "  "<< vec.w<< std::endl;
}

